/****************************************************************
 * Fulguro
 * Copyright (C) 2004 Christophe Clienti
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 ***************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <flgrCoreDataIO.h>
#include <flgrCoreVector.h>
#include <flgrCoreDispatch.h>
#include <flgrCoreSlideWindow.h>
#include <flgrCoreNhbManage.h>
#include "flgrLinearGabor.h"


/*!
 * \defgroup group_fulguro_linear_kernel Convolution Kernel
 * \ingroup group_fulguro_linear
 * Fill neighborhood definition with specific convolution kernel
 * @{
 */


///////////////////////////////////////////////////////////////////////////
// Macro for different type for flgr1d_set_gabor_kernel_dtype function
//////////////////////////////////////////////////////////////////
#define FLGR_MACRO_SET_GABOR_KERNEL_1D(dtype)				\
  FLGR_Vector *vec;							\
  fgFLOAT64 M;								\
  int v;								\
  fgFLOAT64 tmp,tmpx,tmpy,tmpexp,tmpcos;				\
  fgFLOAT64 sigma;							\
  fgFLOAT64 vv;								\
									\
  FLGR_DECLARE_FUNCTION;						\
									\
  vec = flgr_vector_create(nhb->spp,nhb->type);				\
									\
  sigma = sqrt(log(2)/2)/FLGR_PI*((pow(2,bandwidth)+1) /		\
				  (pow(2,bandwidth)-1)*lambda);	\
  									\
  M = (fgFLOAT64) (nhb->length-1);					\
  									\
  for(v=0 ; v<nhb->length ; v++) {					\
    vv=((fgFLOAT64) (v))-M/2;						\
      									\
    tmpx = vv*cos(theta);						\
    tmpy = -vv*sin(theta);						\
    tmpexp = exp(-1*(pow(tmpx,2)+pow(gamma,2)*pow(tmpy,2))		\
		 / (2*pow(sigma,2)));					\
    tmpcos = cos(2*FLGR_PI*tmpx/lambda+phi);				\
    tmp = ((tmpcos*tmpexp)+offset)*amplitude;				\
      									\
    flgr_vector_populate_from_scalar_##dtype(vec, (dtype) tmp);		\
    flgr1d_set_data_vector(nhb,v,vec);					\
  }									\
  									\
  flgr_vector_destroy(vec);						\
  return

void flgr1d_set_gabor_kernel_fgBIT(FLGR_Data1D *nhb,
				   fgFLOAT64 lambda,
				   fgFLOAT64 theta,
				   fgFLOAT64 phi,
				   fgFLOAT64 gamma,
				   fgFLOAT64 bandwidth,
				   fgFLOAT64 offset,
				   fgFLOAT64 amplitude) {
  flgr_no_define_type_function(FLGR_BIT);
}
void flgr1d_set_gabor_kernel_fgUINT8(FLGR_Data1D *nhb,
				     fgFLOAT64 lambda,
				     fgFLOAT64 theta,
				     fgFLOAT64 phi,
				     fgFLOAT64 gamma,
				     fgFLOAT64 bandwidth,
				     fgFLOAT64 offset,
				     fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_1D(fgUINT8);
}
void flgr1d_set_gabor_kernel_fgUINT16(FLGR_Data1D *nhb,
				      fgFLOAT64 lambda,
				      fgFLOAT64 theta,
				      fgFLOAT64 phi,
				      fgFLOAT64 gamma,
				      fgFLOAT64 bandwidth,
				      fgFLOAT64 offset,
				      fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_1D(fgUINT16);
}
void flgr1d_set_gabor_kernel_fgUINT32(FLGR_Data1D *nhb,
				      fgFLOAT64 lambda,
				      fgFLOAT64 theta,
				      fgFLOAT64 phi,
				      fgFLOAT64 gamma,
				      fgFLOAT64 bandwidth,
				      fgFLOAT64 offset,
				      fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_1D(fgUINT32);
}
void flgr1d_set_gabor_kernel_fgINT8(FLGR_Data1D *nhb,
				    fgFLOAT64 lambda,
				    fgFLOAT64 theta,
				    fgFLOAT64 phi,
				    fgFLOAT64 gamma,
				    fgFLOAT64 bandwidth,
				    fgFLOAT64 offset,
				    fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_1D(fgINT8);
}
void flgr1d_set_gabor_kernel_fgINT16(FLGR_Data1D *nhb,
				     fgFLOAT64 lambda,
				     fgFLOAT64 theta,
				     fgFLOAT64 phi,
				     fgFLOAT64 gamma,
				     fgFLOAT64 bandwidth,
				     fgFLOAT64 offset,
				     fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_1D(fgINT16);
}
void flgr1d_set_gabor_kernel_fgINT32(FLGR_Data1D *nhb,
				     fgFLOAT64 lambda,
				     fgFLOAT64 theta,
				     fgFLOAT64 phi,
				     fgFLOAT64 gamma,
				     fgFLOAT64 bandwidth,
				     fgFLOAT64 offset,
				     fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_1D(fgINT32);
}
void flgr1d_set_gabor_kernel_fgFLOAT32(FLGR_Data1D *nhb,
				       fgFLOAT64 lambda,
				       fgFLOAT64 theta,
				       fgFLOAT64 phi,
				       fgFLOAT64 gamma,
				       fgFLOAT64 bandwidth,
				       fgFLOAT64 offset,
				       fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_1D(fgFLOAT32);
}
void flgr1d_set_gabor_kernel_fgFLOAT64(FLGR_Data1D *nhb,
				       fgFLOAT64 lambda,
				       fgFLOAT64 theta,
				       fgFLOAT64 phi,
				       fgFLOAT64 gamma,
				       fgFLOAT64 bandwidth,
				       fgFLOAT64 offset,
				       fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_1D(fgFLOAT64);
}



//////////////////////////////////////////////////////////////////
/*! Create a Gabor Kernel in a neighborhood definition
 *  @param nhb : a pointer to FLGR_Data1D
 *  @param lambda : wavelength of the cosine factor of the gabor funtion
 *  @param theta  : orientation of the normal parallel stripes of the gabor function (rad)
 *  @param phi    : phase offset of the cosine factor of the gabor function (rad)
 *  @param gamma  : ellipticity of the gabor factor (generally 0.2 < gamma < 1)
 *  @param bandwidth : spatial frequency bandwidth of the filter (generally 0.4 < bdwdth < 2.5)
 *  @param offset : offset to apply to gabor function (apply before amplitude)
 *  @param amplitude : amplitude to apply to gabor function
 *  @returns FLGR_RET_OK, ...
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr1d_set_gabor_kernel(FLGR_Data1D *nhb,
				 fgFLOAT64 lambda,
				 fgFLOAT64 theta,
				 fgFLOAT64 phi,
				 fgFLOAT64 gamma,
				 fgFLOAT64 bandwidth,
				 fgFLOAT64 offset,
				 fgFLOAT64 amplitude) {

  FLGR_DECLARE_FUNCTION;
  
  if(nhb==NULL){
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  FLGR_DISPATCH_PROCEDURE(nhb->type,flgr1d_set_gabor_kernel,nhb, lambda,theta,phi,gamma,bandwidth,offset,amplitude);

}
















///////////////////////////////////////////////////////////////////////////
// Macro for different type for flgr2d_set_gabor_kernel_dtype function
//////////////////////////////////////////////////////////////////
#define FLGR_MACRO_SET_GABOR_KERNEL_2D(dtype)				\
  FLGR_Vector *vec;							\
  fgFLOAT64 N,M;							\
  int v,u;								\
  fgFLOAT64 tmp,tmpx,tmpy,tmpexp,tmpcos;				\
  fgFLOAT64 sigma;							\
  fgFLOAT64 uu,vv;							\
									\
  FLGR_DECLARE_FUNCTION;						\
									\
  vec = flgr_vector_create(nhb->spp,nhb->type);				\
									\
  sigma = sqrt(log(2)/2)/FLGR_PI*((pow(2,bandwidth)+1) /		\
				  (pow(2,bandwidth)-1)*lambda);	\
  									\
  N = (fgFLOAT64) (nhb->size_y-1);					\
  M = (fgFLOAT64) (nhb->size_x-1);					\
  									\
  for(u=0 ; u<nhb->size_y ; u++) {					\
    uu=((fgFLOAT64) (u))-N/2;						\
    									\
    for(v=0 ; v<nhb->size_x ; v++) {					\
      vv=((fgFLOAT64) (v))-M/2;						\
      									\
      tmpx = uu*sin(theta)+vv*cos(theta);				\
      tmpy = uu*cos(theta)-vv*sin(theta);				\
      tmpexp = exp(-1*(pow(tmpx,2)+pow(gamma,2)*pow(tmpy,2))		\
		   / (2*pow(sigma,2)));					\
      tmpcos = cos(2*FLGR_PI*tmpx/lambda+phi);				\
      tmp = ((tmpcos*tmpexp)+offset)*amplitude;				\
      									\
      flgr_vector_populate_from_scalar_##dtype(vec, (dtype) tmp);	\
      flgr2d_set_data_vector(nhb,u,v,vec);				\
    }									\
  }									\
  									\
  flgr_vector_destroy(vec);						\
  return

void flgr2d_set_gabor_kernel_fgBIT(FLGR_Data2D *nhb,
				   fgFLOAT64 lambda,
				   fgFLOAT64 theta,
				   fgFLOAT64 phi,
				   fgFLOAT64 gamma,
				   fgFLOAT64 bandwidth,
				   fgFLOAT64 offset,
				   fgFLOAT64 amplitude) {
  flgr_no_define_type_function(FLGR_BIT);
}
void flgr2d_set_gabor_kernel_fgUINT8(FLGR_Data2D *nhb,
				     fgFLOAT64 lambda,
				     fgFLOAT64 theta,
				     fgFLOAT64 phi,
				     fgFLOAT64 gamma,
				     fgFLOAT64 bandwidth,
				     fgFLOAT64 offset,
				     fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_2D(fgUINT8);
}
void flgr2d_set_gabor_kernel_fgUINT16(FLGR_Data2D *nhb,
				      fgFLOAT64 lambda,
				      fgFLOAT64 theta,
				      fgFLOAT64 phi,
				      fgFLOAT64 gamma,
				      fgFLOAT64 bandwidth,
				      fgFLOAT64 offset,
				      fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_2D(fgUINT16);
}
void flgr2d_set_gabor_kernel_fgUINT32(FLGR_Data2D *nhb,
				      fgFLOAT64 lambda,
				      fgFLOAT64 theta,
				      fgFLOAT64 phi,
				      fgFLOAT64 gamma,
				      fgFLOAT64 bandwidth,
				      fgFLOAT64 offset,
				      fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_2D(fgUINT32);
}
void flgr2d_set_gabor_kernel_fgINT8(FLGR_Data2D *nhb,
				    fgFLOAT64 lambda,
				    fgFLOAT64 theta,
				    fgFLOAT64 phi,
				    fgFLOAT64 gamma,
				    fgFLOAT64 bandwidth,
				    fgFLOAT64 offset,
				    fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_2D(fgINT8);
}
void flgr2d_set_gabor_kernel_fgINT16(FLGR_Data2D *nhb,
				     fgFLOAT64 lambda,
				     fgFLOAT64 theta,
				     fgFLOAT64 phi,
				     fgFLOAT64 gamma,
				     fgFLOAT64 bandwidth,
				     fgFLOAT64 offset,
				     fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_2D(fgINT16);
}
void flgr2d_set_gabor_kernel_fgINT32(FLGR_Data2D *nhb,
				     fgFLOAT64 lambda,
				     fgFLOAT64 theta,
				     fgFLOAT64 phi,
				     fgFLOAT64 gamma,
				     fgFLOAT64 bandwidth,
				     fgFLOAT64 offset,
				     fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_2D(fgINT32);
}
void flgr2d_set_gabor_kernel_fgFLOAT32(FLGR_Data2D *nhb,
				       fgFLOAT64 lambda,
				       fgFLOAT64 theta,
				       fgFLOAT64 phi,
				       fgFLOAT64 gamma,
				       fgFLOAT64 bandwidth,
				       fgFLOAT64 offset,
				       fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_2D(fgFLOAT32);
}
void flgr2d_set_gabor_kernel_fgFLOAT64(FLGR_Data2D *nhb,
				       fgFLOAT64 lambda,
				       fgFLOAT64 theta,
				       fgFLOAT64 phi,
				       fgFLOAT64 gamma,
				       fgFLOAT64 bandwidth,
				       fgFLOAT64 offset,
				       fgFLOAT64 amplitude) {
  FLGR_MACRO_SET_GABOR_KERNEL_2D(fgFLOAT64);
}



//////////////////////////////////////////////////////////////////
/*! Create a Gabor Kernel in a neighborhood definition
 *  @param nhb : a pointer to FLGR_Data2D
 *  @param lambda : wavelength of the cosine factor of the gabor funtion
 *  @param theta  : orientation of the normal parallel stripes of the gabor function (rad)
 *  @param phi    : phase offset of the cosine factor of the gabor function (rad)
 *  @param gamma  : ellipticity of the gabor factor (generally 0.2 < gamma < 1)
 *  @param bandwidth : spatial frequency bandwidth of the filter (generally 0.4 < bdwdth < 2.5)
 *  @param offset : offset to apply to gabor function (apply before amplitude)
 *  @param amplitude : amplitude to apply to gabor function
 *  @returns FLGR_RET_OK, ...
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr2d_set_gabor_kernel(FLGR_Data2D *nhb,
				 fgFLOAT64 lambda,
				 fgFLOAT64 theta,
				 fgFLOAT64 phi,
				 fgFLOAT64 gamma,
				 fgFLOAT64 bandwidth,
				 fgFLOAT64 offset,
				 fgFLOAT64 amplitude) {

  FLGR_DECLARE_FUNCTION;

  if(nhb==NULL){
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  FLGR_DISPATCH_PROCEDURE(nhb->type,flgr2d_set_gabor_kernel,nhb, lambda,theta,phi,gamma,bandwidth,offset,amplitude);

}


//!@}
